import * as fs from 'fs';
import * as path from 'path';
import express, { Request, Response } from 'express';
import { OpenAI } from 'openai';
import axios from 'axios';
import FormData from 'form-data';
import { processVideo, prompt } from './videoProcessor';
import { uploadMdToFeishu } from './uploadFs';
import { logger } from './logger';
import { 
  FeishuWebhookBody, 
  FeishuMessage, 
  FeishuResponse, 
  AIMessage, 
  ProcessedMessages,
  AppConfig,
  Logger
} from './types';

// 配置
const config: AppConfig = {
  OPENAI_API_KEY: "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",
  OPENAI_BASE_URL: "https://api.groq.com/openai/v1",
  FEISHU_APP_ID: 'cli_a750f1d07210d00d',
  FEISHU_APP_SECRET: 'KDcAVmk8LhuKc1sQQPEK2baus6PDRQpN',
  VERIFICATION_TOKEN: 'o80fgVXi51Jd2IpZpgzAjn1Y1oirwKU4',
  PORT: 7749
};

// 初始化应用
const app = express();
const client = new OpenAI({
  apiKey: config.OPENAI_API_KEY,
  baseURL: config.OPENAI_BASE_URL
});

// 消息处理记录缓存
const processedMessages: ProcessedMessages = {};
// 消息记录过期时间（单位：秒）
const MESSAGE_EXPIRY = 3600; // 1小时

// 用户会话上下文管理
// 使用用户ID作为键，存储会话上下文
interface SessionContext {
  messages: AIMessage[];       // 消息历史
  lastActivity: number;        // 最后活动时间戳
  videoAnalysisContent?: string; // 视频分析结果
}

// 全局会话管理器
const userSessions: {[userId: string]: SessionContext} = {};

// 会话过期时间（单位：秒）
const SESSION_EXPIRY = 30 * 60; // 30分钟

/**
 * 清理过期的会话
 */
function cleanExpiredSessions(): void {
  const currentTime = Date.now() / 1000;
  Object.keys(userSessions).forEach(userId => {
    if (currentTime - userSessions[userId].lastActivity > SESSION_EXPIRY) {
      logger.info(`清理过期会话: ${userId}`);
      delete userSessions[userId];
    }
  });
}

/**
 * 获取或创建用户会话
 * @param userId 用户ID
 * @returns 用户会话上下文
 */
function getOrCreateSession(userId: string): SessionContext {
  if (!userSessions[userId]) {
    logger.info(`为用户 ${userId} 创建新会话`);
    userSessions[userId] = {
      messages: [],
      lastActivity: Date.now() / 1000
    };
  } else {
    // 更新最后活动时间
    userSessions[userId].lastActivity = Date.now() / 1000;
    logger.info(`更新用户 ${userId} 的会话活动时间`);
    
    // 检查会话数据是否完整
    if (!userSessions[userId].messages) {
      logger.info(`用户 ${userId} 的会话消息数组缺失，重置为空数组`);
      userSessions[userId].messages = [];
    }
    
    // 记录会话上下文状态
    const hasVideoAnalysis = userSessions[userId].videoAnalysisContent ? true : false;
    const messageCount = userSessions[userId].messages.length;
    logger.info(`用户 ${userId} 的会话状态: 消息数量=${messageCount}, 有视频分析=${hasVideoAnalysis}`);
  }
  return userSessions[userId];
}

/**
 * 重置用户会话
 * @param userId 用户ID
 */
function resetSession(userId: string): void {
  if (userSessions[userId]) {
    logger.info(`重置用户会话: ${userId}`);
    userSessions[userId] = {
      messages: [],
      lastActivity: Date.now() / 1000
    };
  }
}

/**
 * 清理过期的消息记录
 */
function cleanExpiredMessages(): void {
  const currentTime = Date.now() / 1000;
  Object.keys(processedMessages).forEach(key => {
    if (currentTime - processedMessages[key] > MESSAGE_EXPIRY) {
      delete processedMessages[key];
    }
  });
}

/**
 * 获取飞书API的租户访问令牌
 * @returns 访问令牌或null
 */
async function getTenantToken(): Promise<string | null> {
  try {
    const response = await axios.post<FeishuResponse>(
      'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/',
      {
        app_id: config.FEISHU_APP_ID,
        app_secret: config.FEISHU_APP_SECRET
      },
      {
        headers: { "Content-Type": "application/json" }
      }
    );
    logger.info(`获取租户访问令牌响应: ${JSON.stringify(response.data)}`);
    return response.data.tenant_access_token || null;
  } catch (error) {
    logger.error(`获取租户访问令牌失败: ${(error as Error).message}`);
    return null;
  }
}

/**
 * 回复飞书消息
 * @param msgId 消息ID
 * @param openId 用户ID
 * @param content 回复内容
 * @returns 回复结果或null
 */
async function feishuReply(msgId: string, openId: string | null, content: string): Promise<any | null> {
  const tenantToken = await getTenantToken();
  if (!tenantToken) {
    return null;
  }

  try {
    const formattedContent = openId ? `<at user_id="${openId}"></at> ${content}` : content;
    const response = await axios.post(
      `https://open.feishu.cn/open-apis/im/v1/messages/${msgId}/reply`,
      {
        msg_type: "text",
        content: JSON.stringify({ text: formattedContent })
      },
      {
        headers: {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${tenantToken}`
        }
      }
    );
    return response.data.code === 0 ? response.data.data : null;
  } catch (error) {
    logger.error(`发送回复失败: ${(error as Error).message}`);
    return null;
  }
}

/**
 * 从AI模型获取回复
 * @param messages 消息数组
 * @param model 模型名称
 * @returns AI回复内容
 */
async function queryAi(messages: AIMessage[], model: string = "qwen-qwq-32b"): Promise<string> {
  try {
    // 将AIMessage转换为OpenAI兼容的类型
    const openAIMessages = messages.map(msg => ({
      role: msg.role as any,
      content: msg.content
    }));
    
    const completion = await client.chat.completions.create({
      model: model,
      messages: openAIMessages as any, // 使用类型断言
      temperature: 0.6,
      max_tokens: 4096
    });
    return completion.choices[0]?.message?.content?.trim() || "无回复内容";
  } catch (error) {
    return `抱歉，我现在无法处理您的请求。错误: ${(error as Error).message}`;
  }
}

/**
 * 从消息内容中提取文本
 * @param messageContent 消息内容
 * @returns 提取的文本
 */
function extractTextFromContent(messageContent: any): string {
  if (typeof messageContent === 'object' && messageContent !== null) {
    let text = messageContent.text || '';
    // 过滤掉 <think> 标签之间的内容
    text = text.replace(/<think>.*?<\/think>/gs, '');
    return text.trim();
  }
  
  const textParts: string[] = [];
  
  function extractText(item: any): string {
    if (typeof item === 'object' && item !== null) {
      let text = item.text || '';
      // 过滤掉 <think> 标签之间的内容
      text = text.replace(/<think>.*?<\/think>/gs, '');
      return text.trim();
    } else if (Array.isArray(item)) {
      return item.map(subitem => extractText(subitem)).join(' ');
    }
    return '';
  }
  
  if (typeof messageContent === 'object' && messageContent !== null) {
    const contentData = messageContent.content || [];
    if (Array.isArray(contentData)) {
      contentData.forEach(item => {
        const text = extractText(item);
        if (text) {
          textParts.push(text);
        }
      });
    }
  } else if (Array.isArray(messageContent)) {
    messageContent.forEach(item => {
      const text = extractText(item);
      if (text) {
        textParts.push(text);
      }
    });
  }
  
  return textParts.join(' ').trim();
}

/**
 * 下载飞书消息中的视频文件
 * @param fileKey 文件Key
 * @param tenantToken 租户令牌
 * @param messageId 消息ID
 * @returns 视频文件路径或null
 */
async function downloadVideo(fileKey: string, tenantToken: string, messageId: string): Promise<string | null> {
  try {
    logger.info(`开始下载视频文件，fileKey: ${fileKey}`);
    
    // 先尝试直接通过资源API下载
    try {
      const response = await axios.get(
        `https://open.feishu.cn/open-apis/im/v1/messages/${messageId}/resources/${fileKey}?type=file`,
        {
          headers: {
            "Authorization": `Bearer ${tenantToken}`,
            "Content-Type": "application/json"
          },
          responseType: 'arraybuffer'
        }
      );

      // 保存视频文件
      const timestamp = new Date().toISOString().replace(/[-:\.]/g, '').replace('T', '_').substr(0, 15);
      const videoPath = `temp_video_${timestamp}.mp4`;
      
      fs.writeFileSync(videoPath, response.data);
      logger.info(`视频文件已通过消息资源API保存到: ${videoPath}`);
      return videoPath;
    } catch (error) {
      logger.info(`通过消息资源API下载失败，尝试使用文件API: ${(error as Error).message}`);
      
      // 尝试通过文件API下载
      const fileResponse = await axios.get(
        `https://open.feishu.cn/open-apis/drive/v1/files/${fileKey}/download`,
        {
          headers: {
            "Authorization": `Bearer ${tenantToken}`
          },
          responseType: 'arraybuffer'
        }
      );
      
      // 保存视频文件
      const timestamp = new Date().toISOString().replace(/[-:\.]/g, '').replace('T', '_').substr(0, 15);
      const videoPath = `temp_video_${timestamp}.mp4`;
      
      fs.writeFileSync(videoPath, fileResponse.data);
      logger.info(`视频文件已通过文件API保存到: ${videoPath}`);
      return videoPath;
    }
  } catch (error) {
    logger.error(`下载视频文件时发生错误: ${(error as Error).message}`);
    logger.error(`异常堆栈: ${(error as Error).stack}`);
    return null;
  }
}

/**
 * 获取消息内容
 * @param messageId 消息ID
 * @param tenantToken 租户令牌
 * @returns 消息内容或null
 */
async function getMessageContent(messageId: string, tenantToken: string): Promise<any | null> {
  try {
    const response = await axios.get(
      `https://open.feishu.cn/open-apis/im/v1/messages/${messageId}`,
      {
        headers: {
          "Authorization": `Bearer ${tenantToken}`,
          "Content-Type": "application/json"
        }
      }
    );
    if (response.status === 200) {
      return response.data.data?.items?.[0] || null;
    }
    return null;
  } catch (error) {
    logger.error(`获取消息内容失败: ${(error as Error).message}`);
    return null;
  }
}

// 中间件
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// 路由处理
app.get('/', (req: Request, res: Response) => {
  res.json({ status: "ok", message: "Feishu Bot Service is running" });
});

app.post('/', async (req: Request, res: Response) => {
  try {
    const body: FeishuWebhookBody = req.body;
    
    // 记录精简的webhook请求信息
    if (body.header) {
      logger.info(`收到webhook请求: event_id=${body.header.event_id}, event_type=${body.header.event_type}`);
    } else {
      logger.info(`收到非标准webhook请求`);
    }
    
    // 处理Challenge验证
    if (body.challenge) {
      logger.info("收到challenge验证请求");
      return res.json({ challenge: body.challenge });
    }
    
    // 检查事件回调
    if (!body.header || !body.event) {
      logger.info("收到非事件回调请求");
      return res.json({ status: "ok", message: "Not an event callback" });
    }
    
    // 检查事件类型
    const eventType = body.header.event_type;
    logger.info(`收到事件类型: ${eventType}`);
    
    // 只处理im.message.receive_v1事件，其他事件直接返回成功
    if (eventType !== 'im.message.receive_v1') {
      logger.info(`收到非消息事件: ${eventType}，直接返回成功`);
      return res.json({ status: "ok", message: "Not a message event" });
    }
    
    // 处理消息
    const message = body.event.message;
    if (!message) {
      logger.info("事件中缺少message字段");
      return res.json({ status: "ok", message: "No message field in event" });
    }

    // 获取消息ID，用于去重处理
    const messageId = message.message_id;
    if (!messageId) {
      logger.info("消息ID为空");
      return res.json({ status: "ok", message: "No valid message ID" });
    }
    
    // 检查消息是否已处理过
    if (processedMessages[messageId]) {
      logger.info(`消息已处理过，跳过: ${messageId}`);
      return res.json({ status: "ok", message: "Message already processed" });
    }

    // 根据消息类型处理
    const messageType = message.message_type;
    logger.info(`处理消息类型: ${messageType}, 消息ID: ${messageId}`);
    
    // 记录从消息中获取的内容，帮助调试
    try {
      const contentStr = message.content || '{}';
      const contentObj = JSON.parse(contentStr);
      const contentSummary = JSON.stringify(contentObj).substring(0, 300);
      logger.info(`消息内容摘要: ${contentSummary}${contentSummary.length >= 300 ? '...' : ''}`);
    } catch (error) {
      logger.info(`提取消息内容摘要失败: ${(error as Error).message}`);
    }

    if (messageType === 'text') {
      // 获取发送者ID
      const senderId = body.event.sender?.sender_id;
      const openId = senderId?.open_id || null;
      
      // 处理文本消息
      const content = message.content;
      if (!content) {
        logger.info("文本内容为空");
        return res.json({ status: "ok", message: "No valid content" });
      }
      
      const replyContent = await handleTextMessage(messageId, content, openId);
      
      if (replyContent) {
        // 发送回复
        if (await feishuReply(messageId, openId, replyContent)) {
          logger.info(`成功回复消息: ${messageId}`);
          processedMessages[messageId] = Date.now() / 1000;
        } else {
          logger.error(`回复消息失败: ${messageId}`);
        }
      }
      
    } else if (messageType === 'media') {
      logger.info("收到媒体消息");
      // 获取发送者ID
      const senderId = body.event.sender?.sender_id;
      const openId = senderId?.open_id || null;
      logger.info(`发送者ID: ${openId}`);
      
      // 解析消息内容
      const content = message.content;
      if (!content) {
        logger.error("媒体消息内容为空");
        return res.json({ status: "ok", message: "No valid content" });
      }
      
      try {
        const contentData = JSON.parse(content);
        const fileKey = contentData.file_key;
        const fileName = contentData.file_name || '';
        
        if (!fileKey) {
          logger.error("未找到文件信息");
          return res.json({ status: "ok", message: "No file information" });
        }
        
        logger.info(`处理视频文件: ${fileName}`);
        
        // 立即回复正在处理的消息
        if (await feishuReply(messageId, openId, "已收到视频，正在处理中，请稍候...")) {
          logger.info("已发送视频处理中提示");
        } else {
          logger.error("发送视频处理中提示失败");
        }
        
        // 异步处理视频
        // 注意：Node.js 原生支持异步处理，不需要额外的线程
        processedMessages[messageId] = Date.now() / 1000;
        
        // 启动异步处理
        (async () => {
          try {
            logger.info("开始处理视频");
            // 获取租户token
            const tenantToken = await getTenantToken();
            if (!tenantToken) {
              throw new Error("获取访问令牌失败");
            }
            
            // 下载视频文件
            const videoPath = await downloadVideo(fileKey, tenantToken, messageId);
            if (!videoPath) {
              throw new Error("下载视频文件失败");
            }
            
            try {
              // 分析视频
              const result = await processVideo(videoPath, prompt);
              
              if (result) {
                // 保存分析结果到markdown文件
                const timestamp = new Date().toISOString().replace(/[-:\.]/g, '').replace('T', '_').substr(0, 15);
                const mdPath = `video_analysis_${timestamp}.md`;
                fs.writeFileSync(mdPath, result, 'utf8');
                
                // 上传到飞书文档
                const docUrl = await uploadMdToFeishu(mdPath);
                
                // 保存视频分析结果到用户会话
                if (openId) {
                  saveVideoAnalysisToSession(openId, result);
                }
                
                // 清理临时文件
                fs.unlinkSync(videoPath);
                fs.unlinkSync(mdPath);
                
                let replyContent: string;
                if (docUrl) {
                  replyContent = `视频分析完成！请查看分析报告：${docUrl}\n\n✅ 您可以直接发送问题，我会基于视频内容回答您的提问。\n✅ 发送"0"可以结束当前会话。`;
                } else {
                  replyContent = "视频分析完成，但创建文档失败，您仍可以向我提问视频内容的问题";
                }
                
                if (await feishuReply(messageId, openId, replyContent)) {
                  logger.info("已发送视频处理结果");
                } else {
                  logger.error("发送视频处理结果失败");
                }
              } else {
                const errorMsg = "视频分析失败";
                await feishuReply(messageId, openId, errorMsg);
              }
            } finally {
              // 确保清理临时文件
              if (fs.existsSync(videoPath)) {
                fs.unlinkSync(videoPath);
              }
            }
          } catch (error) {
            logger.error(`处理视频时发生错误: ${(error as Error).message}`);
            logger.error(`异常堆栈: ${(error as Error).stack}`);
            const errorMsg = `处理视频时发生错误: ${(error as Error).message}`;
            await feishuReply(messageId, openId, errorMsg);
          }
        })();
        
      } catch (error) {
        logger.error(`解析消息内容失败: ${(error as Error).message}`);
        return res.json({ status: "ok", message: "Invalid message content" });
      }
    } else if (messageType === 'post') {
      logger.info("收到富文本消息");
      // 获取发送者ID
      const senderId = body.event.sender?.sender_id;
      const openId = senderId?.open_id || null;
      logger.info(`发送者ID: ${openId}`);
      
      // 获取租户token
      const tenantToken = await getTenantToken();
      if (!tenantToken) {
        logger.error("获取访问令牌失败");
        return res.json({ status: "ok", message: "Failed to get tenant token" });
      }
      
      // 获取消息详情
      const messageDetail = await getMessageContent(messageId, tenantToken);
      if (!messageDetail) {
        logger.error("获取消息详情失败");
        return res.json({ status: "ok", message: "Failed to get message detail" });
      }
      
      // 解析富文本内容，查找视频文件
      try {
        logger.info(`富文本消息ID: ${messageId}`);
        logger.info(`富文本消息类型: ${messageType}`);
        logger.info(`富文本body类型: ${typeof messageDetail.body}`);
        logger.info(`富文本content类型: ${typeof messageDetail.body?.content}`);
        
        // 检查是否有文件附件
        const postContent = messageDetail.body?.content;
        if (!postContent) {
          logger.error("富文本内容为空");
          return res.json({ status: "ok", message: "No post content" });
        }
        
        // 尝试解析JSON内容
        let parsedContent;
        try {
          parsedContent = typeof postContent === 'string' ? JSON.parse(postContent) : postContent;
          logger.info(`解析后的富文本内容类型: ${typeof parsedContent}`);
          logger.info(`富文本内容结构: ${JSON.stringify(parsedContent).substring(0, 500)}`);
        } catch (e) {
          logger.error(`解析富文本内容失败: ${(e as Error).message}`);
          return res.json({ status: "ok", message: "Failed to parse post content" });
        }
        
        // 查找视频文件
        let fileKey = null;
        let fileName = null;
        
        // 更复杂的查找逻辑，处理不同结构的富文本消息
        function findVideoInContent(content: any): { fileKey: string | null, fileName: string | null } {
          // 如果是一个对象
          if (content && typeof content === 'object' && !Array.isArray(content)) {
            // 直接检查file_key属性
            if (content.file_key) {
              return {
                fileKey: content.file_key,
                fileName: content.file_name || content.fileName || '未命名文件'
              };
            }
            
            // 检查视频类型
            if ((content.type === 'video' || content.tag === 'video') && content.video_id) {
              return {
                fileKey: content.video_id,
                fileName: '视频内容'
              };
            }
            
            // 检查media类型
            if ((content.type === 'media' || content.tag === 'media') && (content.file_key || content.fileKey)) {
              return {
                fileKey: content.file_key || content.fileKey,
                fileName: content.file_name || content.fileName || '媒体文件'
              };
            }
            
            // 遍历所有属性
            for (const key in content) {
              const result = findVideoInContent(content[key]);
              if (result.fileKey) {
                return result;
              }
            }
          }
          
          // 如果是数组
          if (Array.isArray(content)) {
            for (const item of content) {
              const result = findVideoInContent(item);
              if (result.fileKey) {
                return result;
              }
            }
          }
          
          return { fileKey: null, fileName: null };
        }
        
        // 使用递归函数查找视频
        const videoInfo = findVideoInContent(parsedContent);
        fileKey = videoInfo.fileKey;
        fileName = videoInfo.fileName;
        
        logger.info(`解析富文本结果: fileKey=${fileKey}, fileName=${fileName}`);
        
        if (!fileKey) {
          logger.error("未在富文本消息中找到视频文件");
          await feishuReply(messageId, openId, "未能在消息中找到视频文件，请直接发送视频文件");
          return res.json({ status: "ok", message: "No video file found in post" });
        }
        
        logger.info(`从富文本中找到视频: ${fileName}, fileKey: ${fileKey}`);
        
        // 立即回复正在处理的消息
        if (await feishuReply(messageId, openId, "已收到视频，正在处理中，请稍候...")) {
          logger.info("已发送视频处理中提示");
        } else {
          logger.error("发送视频处理中提示失败");
        }
        
        // 标记消息为已处理
        processedMessages[messageId] = Date.now() / 1000;
        
        // 启动异步处理
        (async () => {
          try {
            // 下载视频文件
            const videoPath = await downloadVideo(fileKey, tenantToken, messageId);
            if (!videoPath) {
              throw new Error("下载视频文件失败");
            }
            
            try {
              // 分析视频
              const result = await processVideo(videoPath, prompt);
              
              if (result) {
                // 保存分析结果到markdown文件
                const timestamp = new Date().toISOString().replace(/[-:\.]/g, '').replace('T', '_').substr(0, 15);
                const mdPath = `video_analysis_${timestamp}.md`;
                fs.writeFileSync(mdPath, result, 'utf8');
                
                // 上传到飞书文档
                const docUrl = await uploadMdToFeishu(mdPath);
                
                // 保存视频分析结果到用户会话
                if (openId) {
                  saveVideoAnalysisToSession(openId, result);
                }
                
                // 清理临时文件
                fs.unlinkSync(videoPath);
                fs.unlinkSync(mdPath);
                
                let replyContent: string;
                if (docUrl) {
                  replyContent = `视频分析完成！请查看分析报告：${docUrl}\n\n✅ 您可以直接发送问题，我会基于视频内容回答您的提问。\n✅ 发送"0"可以结束当前会话。`;
                } else {
                  replyContent = "视频分析完成，但创建文档失败，您仍可以向我提问视频内容的问题";
                }
                
                if (await feishuReply(messageId, openId, replyContent)) {
                  logger.info("已发送视频处理结果");
                } else {
                  logger.error("发送视频处理结果失败");
                }
              } else {
                const errorMsg = "视频分析失败";
                await feishuReply(messageId, openId, errorMsg);
              }
            } finally {
              // 确保清理临时文件
              if (fs.existsSync(videoPath)) {
                fs.unlinkSync(videoPath);
              }
            }
          } catch (error) {
            logger.error(`处理视频时发生错误: ${(error as Error).message}`);
            logger.error(`异常堆栈: ${(error as Error).stack}`);
            const errorMsg = `处理视频时发生错误: ${(error as Error).message}`;
            await feishuReply(messageId, openId, errorMsg);
          }
        })();
        
      } catch (error) {
        logger.error(`解析富文本消息内容失败: ${(error as Error).message}`);
        return res.json({ status: "ok", message: "Invalid post content" });
      }
    } else {
      logger.info(`不支持的消息类型: ${messageType}`);
      return res.json({ status: "ok", message: "Unsupported message type" });
    }
    
    return res.json({ status: "ok" });
  } catch (error) {
    logger.error(`处理请求异常: ${(error as Error).message}`);
    logger.error(`异常堆栈: ${(error as Error).stack}`);
    return res.json({ status: "error", message: (error as Error).message });
  }
});

app.get('/health', (req: Request, res: Response) => {
  res.json({ status: "ok", message: "Feishu Bot Service is healthy" });
});

// AI审核提示词
const AUDIT_PROMPT = `
请作为开发伦理顾问，审查提交的开发需求和相关资料。重点关注法律风险，对创新保持开放态度，用通俗易懂的语言给出判断。

请评估以下方面：

1. 数据获取：个人敏感数据获取、破解付费墙等行为
2. 金融风险：赌博应用、破解支付系统、金融诈骗工具
3. 政治内容：涉及任何政治、政策、政府、官媒相关的信息。
4. 文件伪造：伪造证件、官方印章等
5. 数据采集：评估数据来源、采集规模和用途，允许采集美团等平台的商家公开数据
6. 网站仿制：区分学习目的和欺诈行为
7. 自动化工具：区分效率提升和恶意绕过

合法且风险可控的情况下，允许：
- 公开数据的合理采集和分析（如美团等平台的商家公开信息）
- 自有系统的安全测试
- 学习目的的功能复制
- 提高效率的自动化工具

请直接输出以下JSON格式的评估结果（使用简明的中文）：

\`\`\`json
{
  "违规情况": "yes/no",
  "风险评级": "高/中/低",
  "主要问题": "用通俗语言描述核心问题",
}
\`\`\`
`;

/**
 * 将AI回复保存为Markdown文件并上传到飞书
 * @param question 用户问题
 * @param answer AI回答
 * @param title 文档标题
 * @returns 带有文档链接的回复或原始回复
 */
async function saveAndUploadAIResponse(question: string, answer: string, title: string = "AI回复"): Promise<string> {
  try {
    // 检查答案是否有效
    if (!answer || answer.trim() === '' || answer.includes("404 Not Found")) {
      logger.error(`无效的AI回复内容，包含错误信息或为空: ${answer.substring(0, 100)}`);
      return "抱歉，AI回复生成失败，请稍后重试。";
    }
    
    // 生成文件名
    const timestamp = new Date().toISOString().replace(/[-:\.]/g, '').replace('T', '_').substr(0, 15);
    const mdFilename = `ai_response_${timestamp}.md`;
    
    // 构建Markdown内容（只包含AI回复内容）
    const mdContent = answer.trim();
    
    // 记录内容长度和前100个字符
    logger.info(`准备保存的AI回复长度: ${mdContent.length}, 前100个字符: ${mdContent.substring(0, 100)}`);
    
    // 保存到文件
    fs.writeFileSync(mdFilename, mdContent, 'utf8');
    logger.info(`已将AI回复保存到文件: ${mdFilename}, 文件大小: ${fs.statSync(mdFilename).size} 字节`);
    
    // 上传到飞书文档
    const docUrl = await uploadMdToFeishu(mdFilename);
    
    // 清理临时文件
    try {
      fs.unlinkSync(mdFilename);
      logger.info(`已删除临时文件: ${mdFilename}`);
    } catch (e) {
      logger.error(`删除临时文件失败: ${mdFilename}, 错误: ${(e as Error).message}`);
    }
    
    // 如果上传成功，返回带链接的回复，否则返回原文本
    if (docUrl) {
      logger.info(`已将AI回复上传到飞书文档: ${docUrl}`);
      // 返回简化的回复格式，只包含文档链接
      return `${docUrl}`;
    } else {
      logger.error(`上传AI回复到飞书文档失败，返回原始回复`);
      return answer;
    }
  } catch (error) {
    logger.error(`处理AI回复文档时出错: ${(error as Error).message}, 堆栈: ${(error as Error).stack}`);
    return answer;
  }
}

// 处理文本消息
async function handleTextMessage(messageId: string, content: string, openId: string | null): Promise<string | null> {
  try {
    // 提取文本
    const messageContent = JSON.parse(content);
    let extractedText = extractTextFromContent(messageContent).replace(/@_user_1 /g, '');
    
    // 过滤掉 JSON 字符串中的 <think> 标签内容
    extractedText = extractedText.replace(/<think>.*?<\/think>/gs, '');
    
    if (!extractedText) {
      logger.info("提取的文本为空");
      return null;
    }

    logger.info(`处理文本消息: ${extractedText}`);
    
    // 如果用户发送了"0"，则重置会话
    if (extractedText.trim() === "0") {
      if (openId) {
        resetSession(openId);
        return "会话已重置，您可以开始新的对话了。";
      }
      return null;
    }
    
    // 获取用户会话
    const session = openId ? getOrCreateSession(openId) : null;
    logger.info(`用户 ${openId} 的会话状态: ${session ? `存在，消息数量=${session.messages.length}, 是否有视频分析=${session.videoAnalysisContent ? 'true' : 'false'}` : '不存在'}`);
    
    let replyContent = "";
    
    // 如果会话中有视频分析内容，则使用视频内容作为上下文
    if (session && session.videoAnalysisContent) {
      try {
        // 如果没有任何消息，或者没有系统消息，先添加系统消息
        if (session.messages.length === 0 || !session.messages.some(msg => msg.role === "system")) {
          logger.info(`为用户 ${openId} 添加系统消息，包含视频分析内容`);
          // 确保移除旧的系统消息
          session.messages = session.messages.filter(msg => msg.role !== "system");
          // 添加新的系统消息
          session.messages.unshift({ 
            role: "system", 
            content: `以下是视频内容的分析结果，请基于这些内容回答用户问题：\n\n${session.videoAnalysisContent}`
          });
          logger.info(`已添加系统消息，当前消息数量: ${session.messages.length}`);
        }
        
        // 添加用户消息
        session.messages.push({ role: "user", content: extractedText });
        logger.info(`添加用户消息到会话，消息内容: "${extractedText.substring(0, 50)}${extractedText.length > 50 ? '...' : ''}"`);
        logger.info(`当前会话消息总数: ${session.messages.length}`);
        
        // 将消息发送给AI处理
        logger.info(`发送消息到AI进行处理，共${session.messages.length}条消息`);
        replyContent = await queryAi(session.messages);
        logger.info(`获取到AI回复，长度: ${replyContent.length}`);
        
        // 添加AI回复到会话
        session.messages.push({ role: "assistant", content: replyContent });
        
        // 如果会话过长，裁剪掉最早的消息
        if (session.messages.length > 12) {
          // 保留系统消息和最近的会话
          const systemMessages = session.messages.filter(msg => msg.role === "system");
          const recentMessages = session.messages.filter(msg => msg.role !== "system").slice(-10);
          session.messages = [...systemMessages, ...recentMessages];
          logger.info(`会话消息过长，已裁剪至系统消息+最近10条对话，共${session.messages.length}条`);
        }
      } catch (error) {
        logger.error(`处理视频分析会话时出错: ${(error as Error).message}, 堆栈: ${(error as Error).stack}`);
        // 如果处理视频分析会话时出错，回退到AUDIT_PROMPT处理
        logger.info(`回退到AUDIT_PROMPT处理用户消息: ${extractedText}`);
        const aiMessages: AIMessage[] = [
          { role: "system", content: AUDIT_PROMPT },
          { role: "user", content: extractedText }
        ];
        replyContent = await queryAi(aiMessages);
      }
    } else {
      // 无视频分析内容的情况下，使用审核提示词
      logger.info(`用户 ${openId} 没有视频分析内容，使用默认审核提示词处理`);
      const aiMessages: AIMessage[] = [
        { role: "system", content: AUDIT_PROMPT },
        { role: "user", content: extractedText }
      ];
      
      replyContent = await queryAi(aiMessages);
    }
    
    // 检查回复内容是否有效
    if (!replyContent || replyContent.trim() === '' || replyContent.includes("404 Not Found")) {
      logger.error(`无效的AI回复: ${replyContent ? replyContent.substring(0, 100) : '空回复'}`);
      return "抱歉，AI回复生成失败，请稍后重试。";
    }
    
    // 将AI回复保存为Markdown文件并上传到飞书
    return await saveAndUploadAIResponse(extractedText, replyContent, "AI回复");
  } catch (error) {
    logger.error(`处理文本消息异常: ${(error as Error).message}`);
    logger.error(`异常堆栈: ${(error as Error).stack}`);
    return "处理消息时发生错误，请稍后重试。";
  }
}

// 定期清理过期消息和会话
setInterval(cleanExpiredMessages, 600000); // 每10分钟清理一次
setInterval(cleanExpiredSessions, 600000); // 每10分钟清理一次

// 启动服务器
const PORT = config.PORT;
app.listen(PORT, () => {
  console.log(`服务器已在端口 ${PORT} 上启动`);
  logger.info(`服务器已在端口 ${PORT} 上启动`);
  logger.info(`已启用上下文会话功能，支持视频分析后的持续对话`);
  logger.info(`用户会话将在 ${SESSION_EXPIRY / 60} 分钟不活动后自动清理`);
});

/**
 * 保存视频分析结果到用户会话
 * @param userId 用户ID
 * @param analysisContent 分析内容
 */
function saveVideoAnalysisToSession(userId: string, analysisContent: string): void {
  if (!userId || !analysisContent) {
    logger.error(`保存视频分析结果失败: userId=${userId}, 内容长度=${analysisContent ? analysisContent.length : 0}`);
    return;
  }
  
  const session = getOrCreateSession(userId);
  
  // 清空之前的会话消息，确保从新的视频分析开始
  session.messages = [];
  
  // 保存视频分析内容
  session.videoAnalysisContent = analysisContent;
  
  // 添加系统消息，设置上下文
  session.messages.push({ 
    role: "system", 
    content: `以下是视频内容的分析结果，请基于这些内容回答用户问题：\n\n${analysisContent}`
  });
  
  logger.info(`已保存视频分析结果到用户会话: ${userId}, 内容长度=${analysisContent.length}, 初始化消息数量=${session.messages.length}`);
} 